#ifndef RENDERER_HPP
#define RENDERER_HPP

#include "SDL.h"
#include "libmath.hpp"
#include "window.hpp"
#include "texture.hpp"
#include "sdltool.hpp"
#include "font.hpp"

namespace sdl_engine {

class Renderer {
public:
    class CreateInfo {
    public:
        friend class Renderer;

        inline CreateInfo& SetFlags(u32 flags) { flags_ = flags; return *this; }

    private:
        u32 flags_ = SDL_RENDERER_ACCELERATED |
                     SDL_RENDERER_PRESENTVSYNC |
                     SDL_RENDERER_TARGETTEXTURE;
    };

    Renderer(const CreateInfo& createInfo);
    Renderer(const Renderer& o) = delete;
    ~Renderer();

    inline operator bool() const { return renderer_; }

    Renderer& operator=(const Renderer& o) = delete;

    void SetDrawColor(const Color& color);
    void Clear();
    void RenderPresent();

    inline void SetDrawBlendMode(SDL_BlendMode mode) {
        SDL_SetRenderDrawBlendMode(renderer_, mode);
    }
    void SetTarget(Texture* texture);
    void SetTarget2Default();

    void SetViewport(int x, int y, int w, int h);
    Rect GetViewport() const;

    inline void SetScale(float x, float y) {
        SDL_RenderSetScale(renderer_, x, y);
    }

    inline void SetClipRect(const Rect& rect) {
        SDL_Rect area = Rect2SDL(rect);
        SDL_RenderSetClipRect(renderer_, &area);
    }
    inline Rect GetClipRect() const {
        SDL_Rect area;
        SDL_RenderGetClipRect(renderer_, &area);
        return rect(area.x, area.y, area.w, area.h);
    }
    inline void SetClipRect2Default() {
        SDL_RenderSetClipRect(renderer_, nullptr);
    }

    void RenderPoint(const Point& p);
    void RenderLine(const Point& p1, const Point& p2);
    void RenderFillRect(const Rect& rect);
    void RenderRect(const Rect& rect);
    void RenderUTF8(Font& font,
                    const std::string& text,
                    const Point& position,
                    const Color& color);

    void RenderCopy(Texture* texture,
                    const Point& position,
                    const Size& s = size(0, 0),
                    const std::optional<Color>& = std::nullopt);
    void RenderCopyTo(Texture* src, Texture* dst,
                      const Point& position,
                      const Size& s = size(0, 0),
                      const std::optional<Color>& = std::nullopt);

    void RenderCopy(SubTexture* texture,
                    const Point& position,
                    const Size& s = size(0, 0),
                    const std::optional<Color>& = std::nullopt);

    void RenderCopyTo(SubTexture* src, Texture* dst,
                      const Point& position,
                      const Size& s = size(0, 0),
                      const std::optional<Color>& = std::nullopt);

    SDL_Renderer* Raw() { return renderer_; }

private:
    SDL_Renderer* renderer_ = nullptr;

    void setTextureColor(Texture&, const Color&);
};

inline SDL_BlendMode ComposeCustomBlendMode(
    SDL_BlendFactor srcColorFactor,
    SDL_BlendFactor dstColorFator,
    SDL_BlendOperation colorOperation,
    SDL_BlendFactor srcAlphaFactor,
    SDL_BlendFactor dstAlphaFactor,
    SDL_BlendOperation alphaOperation
) {
    return SDL_ComposeCustomBlendMode(
               srcColorFactor, dstColorFator, colorOperation,
               srcAlphaFactor, dstAlphaFactor, alphaOperation
           );
}

}

#endif
